#ifndef __M_THREADPOOL_H__
#define __M_THREADPOOL_H__
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <vector>
#include <functional>
#include <future>
#include <memory>

using functor = std::function<void(void)>;
namespace csjmq
{
    class ThreadPool
    {
    public:
        using ptr = std::shared_ptr<ThreadPool>;
        ThreadPool(int threadNum = 1)
            : _stop(false)
        {
            for (int i = 0; i < threadNum; i++)
            {
                _threads.emplace_back(&ThreadPool::entry, this);
            }
        }

        // 在进行push任务的时候，我们并不知道用户传入的函数的参数是什么和返回值
        // 我们要使用不定参来传递，push函数内部会将传入进来的函数封装成为一个异步任务（packaged_task）
        // 使用lambda生成一个可调用对象（内部异步执行），抛入到任务池中，交给线程来执行
        template <class F, class... Args>
        auto push(F &&func, Args &&...args) -> std::future<decltype(func(args...))>
        // decltype依靠函数名和参数来推导函数的返回值
        {
            // 1.将传入的函数封装成一个packaged_task任务
            using return_type = decltype(func(args...));
            auto tmp_func = std::bind(std::forward<F>(func), std::forward<Args>(args)...);
            auto task = std::make_shared<std::packaged_task<return_type()>>(tmp_func);
            std::future<return_type> fu = task->get_future();

            // 2.构造一个lambda匿名函数（捕捉任务对象），函数内执行任务对象
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _taskpool.push_back([task]()
                                    { (*task)(); });
                // 3.将该匿名函数对象抛入任务池中去
                _cv.notify_one(); // 唤醒线程执行任务
            }
            return fu;
        }
        void stop()
        {
            if (_stop == true)
                return;
            _stop = true;
            _cv.notify_all();             // 将所有的线程唤醒
            for (auto &thread : _threads) // 将所有的线程回收了
            {
                thread.join();
            }
        }
        ~ThreadPool()
        {
            stop();
        }

    private:
        // 线程入口函数 -- 不断的从任务池中取出任务执行
        void entry()
        {
            while (!_stop)
            {
                std::vector<functor> tmp_taskpool;
                {
                    // 加锁
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 等待任务池不为空，或者_stop被置位时返回
                    _cv.wait(lock, [this]()
                             { return _stop || !_taskpool.empty(); });
                    // 第二个参数返回值为false，表示阻塞该线程，释放lock
                    tmp_taskpool.swap(_taskpool);
                }
                // 执行任务
                for (auto &task : tmp_taskpool)
                {
                    task();
                }
            }
        }

    private:
        std::atomic<bool> _stop;
        std::mutex _mutex;                 // 锁
        std::condition_variable _cv;       // 信号量
        std::vector<std::thread> _threads; // 线程池
        std::vector<functor> _taskpool;    // 任务池
    };
}

#endif